package com.rw.engine.frequency.interceptor;

import com.rw.engine.frequency.anno.FrequencyLimit;
import com.rw.engine.frequency.exception.FrequencyExceptionEnum;
import com.rw.engine.frequency.exception.FrequencyLimitException;
import com.rw.tool.util.string.StringFormatter;
import com.rw.tool.util.string.StringUtil;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;

/**
 * 默认的频率限制器
 */
public class DefaultFrequencyFilterRule implements FrequencyFilterRule {
    /**
     * 存入缓存中的计数key模板
     */
    private static final String COUNT_KEY_TEMPLATE = "spring.engine.frequency.token.{}.count";

    /**
     * 存入缓存中的最小间隔key模板
     */
    private static final String INTERVAL_KEY_TEMPLATE = "spring.engine.frequency.token.{}.interval";

    /**
     * token获取器
     */
    private final TokenGetter tokenGetter;

    /**
     * 频率缓存器
     */
    private final FrequencyCache frequencyCache;

    /**
     * 公共频率缓存器
     */
    private static final FrequencyCache globalFrequencyCache = new DefaultFrequencyCache();

    /**
     * 构建自定义的频率限制器
     * 记住要把它加入Spring容器中才会生效
     *
     * @param tokenGetter    token获取器
     * @param frequencyCache 频率缓存器
     */
    public DefaultFrequencyFilterRule(TokenGetter tokenGetter, FrequencyCache frequencyCache) {
        this.tokenGetter = tokenGetter;
        this.frequencyCache = frequencyCache;
    }

    /**
     * 构建默认的频率限制器
     * token直接采用ip获取，频率缓存采用Water-Tool中的CacheHashMap
     */
    public DefaultFrequencyFilterRule() {
        this(ServletRequest::getRemoteAddr, globalFrequencyCache);
    }


    @Override
    public void filter(FrequencyLimit frequencyLimit, HttpServletRequest request) {
        // 获取要验证的标记
        String token = tokenGetter.getToken(request);
        // token获取失败
        if (StringUtil.isBlank(token)) {
            throw new IllegalArgumentException("token is empty!");
        }

        // 获取上次间隔的key值
        String intervalKey = getIntervalKey(token);

        // 两次访问时间间隔不能太短
        if (frequencyCache.getValue(intervalKey) != null) {
            throw new FrequencyLimitException("Last request was too closed!", FrequencyExceptionEnum.TOO_CLOSED);
        }

        // 设置本次访问的时间
        frequencyCache.setValue(intervalKey, 1, frequencyLimit.minInterval(), frequencyLimit.minIntervalUtil());

        // 获取累计计数的key值
        String countKey = getCountKey(token);
        // 获取累计的次数
        Integer cacheValue = frequencyCache.getValue(countKey);

        // 如果还没有记录
        if (cacheValue == null) {
            frequencyCache.setValue(countKey, 1, frequencyLimit.requestsLimitTime(), frequencyLimit.requestsLimitTimeUnit());
            return;
        }

        // 超过限制
        if (cacheValue + 1 >= frequencyLimit.requestsLimit()) {
            throw new FrequencyLimitException("Too many requests in a short period!",
                    FrequencyExceptionEnum.TOO_MANY_REQUESTS);
        }

        // 积累数量
        frequencyCache.setValue(countKey, cacheValue + 1);
    }

    /**
     * 获取缓存中记录累计数量的位置
     * 如果需要自定义位置，可以覆写
     *
     * @param token token
     * @return 缓存地址
     */
    protected String getCountKey(String token) {
        return StringFormatter.format(COUNT_KEY_TEMPLATE, token);
    }

    /**
     * 获取缓存中记录上次时间的位置
     * 如果需要自定义位置，可以覆写
     *
     * @param token token
     * @return 缓存地址
     */
    protected String getIntervalKey(String token) {
        return StringFormatter.format(INTERVAL_KEY_TEMPLATE, token);
    }
}
